<?php

namespace common\behaviors;

use common\models\File;
use Imagine\Exception\RuntimeException;
use Imagine\Image\Box;
use Imagine\Image\BoxInterface;
use Imagine\Image\Color;
use Imagine\Image\ImageInterface;
use Imagine\Image\Point;
use Yii;
use yii\base\Behavior;
use yii\db\BaseActiveRecord;
use yii\helpers\ArrayHelper;
use yii\helpers\FileHelper;
use yii\imagine\Image;

class ImageResizeBehavior extends Behavior
{
    /**
     * @var array 图片尺寸
     */
    private $_sizes = array();

    /**
     * 获取图片尺寸
     *
     * @return array
     */
    public function getSizes()
    {
        return $this->_sizes;
    }

    /**
     * @return array
     */
    public function events()
    {
        return [
            BaseActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave',
        ];
    }

    /**
     * 设置图片尺寸
     *
     * @param array $sizes
     */
    public function setSizes($sizes)
    {
        foreach ($sizes as $name => $size) {
            if (empty($size)) {
                $this->deleteSize($name);
                continue;
            }
            $crop = isset($size[2]) ? $size[2] : false;
            $this->setSize($name, $size[0], $size[1], $crop);
        }
    }

    /**
     * 删除尺寸
     *
     * @param array $names
     */
    public function deleteSize($names)
    {
        foreach ((array)$names as $name) {
            unset($this->_sizes[$name]);
        }
    }

    /**
     * 设置一个尺寸
     *
     * @param string $name 尺寸名
     * @param integer $weight 宽
     * @param integer $height 高
     * @param boolean $crop 是否裁剪
     */
    public function setSize($name, $weight, $height, $crop = false)
    {
        $this->_sizes[$name] = array($weight, $height, $crop);
    }

    /**
     * 获取一个尺寸
     *
     * @param string $name
     * @return array|null
     */
    public function getSize($name)
    {
        if (isset($this->_sizes[$name])) {
            return $this->_sizes[$name];
        } else {
            return null;
        }
    }

    /**
     * @param \yii\base\ModelEvent $event
     */
    public function beforeSave($event)
    {
        if (!$event->isValid) {
            return;
        }

        /** @var \common\models\File $owner */
        $owner = $this->owner;
        if (!$owner instanceof File) {
            return;
        }

        if ($owner->isImage()) {
            $result = $this->resizeImage($this->_sizes, $metaSizes);
            if ($metaSizes) {
                $owner->updateMeta('sizes', $metaSizes);
                if ($result instanceof \Exception) {
                    throw $result;
                }
            }
        }
    }

    /**
     * 缩放图片
     *
     * @param array $sizes 图片尺寸
     * @param array $metaSizes
     * @return array
     */
    public function resizeImage($sizes, &$metaSizes = [])
    {
        /** @var File $owner */
        $owner = $this->owner;
        $filePath = $owner->getPath();

        $parts = pathinfo($filePath);
        $filename = $parts['filename'];
        $ext = $parts['extension'];

        $image = Image::getImagine()->open($filePath);
        $box = $image->getSize();
        $sWidth = $box->getWidth();
        $sHeight = $box->getHeight();

        $baseUri = str_replace(basename($owner->uri), '', $owner->uri);
        if ('/' !== substr($baseUri, -1)) {
            $baseUri .= '/';
        }

        foreach ($sizes as $name => $size) {
            if (!isset($size[2])) {
                $size[2] = false;
            }

            list($width, $height, $crop) = $size;

            $width = abs(intval($width));
            $height = abs(intval($height));

            if (!$width || !$height) {
                continue;
            }

            if ($sWidth < $width && $sHeight < $height
                && ($name !== File::IMAGE_THUMBNAIL || $name !== File::IMAGE_ORIGIN)
            ) {
                continue;
            }

            if (!$crop) {
                $thumbnail = $image->thumbnail(new Box($width, $height));
            } else {
                $thumbnail = $image->thumbnail(new Box($width, $height), ImageInterface::THUMBNAIL_OUTBOUND);
            }

            $imageSize = $thumbnail->getSize();

            if ($name === File::IMAGE_ORIGIN) {
                $saveFilePath = $owner->getPath();
                $uri = $owner->uri;
            } else {
                $uri = $baseUri . "{$filename}-" . $imageSize->getWidth() . 'x'. $imageSize->getHeight() .".{$ext}";
                $saveFilePath = $owner->localPath($uri);
            }

            try {
                if ($name !== File::IMAGE_THUMBNAIL) {
                    $this->processImage($thumbnail);
                }

                $metaSizes[$name] = [
                    'uri' => $uri,
                    'width' => $imageSize->getWidth(),
                    'height' => $imageSize->getHeight(),
                    'crop' => (bool)$crop,
                ];
                $thumbnail->save($saveFilePath);
            } catch (\Exception $e) {
                return $e;
            }
        }

        if (!isset($metaSizes[File::IMAGE_ORIGIN])) {
            $imageSize = $image->getSize();
            $metaSizes[File::IMAGE_ORIGIN] = [
                'uri' => $owner->uri,
                'width' => $imageSize->getWidth(),
                'height' => $imageSize->getHeight(),
                'crop' => false,
            ];
        }

        return $metaSizes;
    }

    public function processImage(ImageInterface $image)
    {
        $option = Yii::$app->get('option');
        $watermarkType = $option->get('file.watermark_type', 0);
        if ($watermarkType == 0) {
            return;
        }

        $options = $option->get('file.watermark_options', []);
        if (!$options) {
            return;
        }
        $position = ArrayHelper::getValue($options, 'position', 'center');
        $distanceX = ArrayHelper::getValue($options, 'distance_x', '10');
        $distanceY = ArrayHelper::getValue($options, 'distance_y', '10');
        $opacity = ArrayHelper::getValue($options, 'opacity', '100');

        $imageBox = $image->getSize();

        if ($watermarkType == 1) {
            $imageUrl = ArrayHelper::getValue($options, 'image_url', false);
            if (!$imageUrl) {
                return;
            }
            if (strpos($imageUrl, 'http://') === 0) {
                $cacheDir = Yii::getAlias('@runtime') . '/temp/';
                $cacheFile = $cacheDir . md5($imageUrl);
                if (!is_file($cacheFile)) {
                    $file = file_get_contents($imageUrl);
                    if (FileHelper::createDirectory($cacheDir)) {
                        file_put_contents($cacheFile, $file);
                    } else {
                        $cacheFile = null;
                    }
                }

                if (isset($cacheFile)) {
                    try {
                        $watermark = Image::getImagine()->open($cacheFile);
                    } catch (RuntimeException $e) {
                        Yii::trace($e->getMessage());
                        return;
                    }
                    $point = $this->calculatePoint($imageBox, $watermark->getSize(), $position, $distanceX, $distanceY);
                    Yii::trace($point);
                    $image->paste($watermark, $point);
                }
            }

        } elseif ($watermarkType == 2) {
            $fontText = ArrayHelper::getValue($options, 'font_text', false);
            if (!$fontText) {
                return;
            }
            $fontSize = ArrayHelper::getValue($options, 'font_size', 12);
            $fontColor = ArrayHelper::getValue($options, 'font_color', 'fff');
            $fontType = ArrayHelper::getValue($options, 'font_type', '微软雅黑');
            $fontAngle = ArrayHelper::getValue($options, 'angle', 0);

            $font = Image::getImagine()->font(Yii::getAlias($this->fontFileMap($fontType)), $fontSize, new Color($fontColor, intval($opacity)));
            $fontBox = $font->box($fontText, $fontAngle);
            $point = $this->calculatePoint($imageBox, $fontBox, $position, $distanceX, $distanceY);
            $image->draw()->text($fontText, $font, $point, $fontAngle);
        }
    }

    protected function fontFileMap($fontType)
    {
        $data = [
            '微软雅黑' => '@common/source/fonts/msyh.ttf',
        ];
        return isset($data[$fontType]) ? $data[$fontType] : $data['微软雅黑'];
    }

    protected function calculatePoint(BoxInterface $imageBox, BoxInterface $box, $position = 'center', $offsetX = 0, $offsetY = 0)
    {
        if (!$imageBox->contains($box)) {
            $box = $imageBox;
        }
        switch ($position) {
            case 'northWest':
                $x = $offsetX;
                $y = $offsetY;
                break;
            case 'west':
                $x = $offsetX;
                $y = ceil(($imageBox->getHeight() - $box->getHeight()) / 2);
                break;
            case 'southWest':
                $x = $offsetX;
                $y = $imageBox->getHeight() - $box->getHeight() - $offsetY;
                break;
            case 'north':
                $x = ceil(($imageBox->getWidth() - $box->getWidth()) / 2);
                $y = $offsetY;
                break;
            case 'south':
                $x = ceil(($imageBox->getWidth() - $box->getWidth()) / 2);
                $y = $imageBox->getHeight() - $box->getHeight() - $offsetY;
                break;
            case 'northEast':
                $x = $imageBox->getWidth() - $box->getWidth() - $offsetX;
                $y = $offsetY;
                break;
            case 'east':
                $x = $imageBox->getWidth() - $box->getWidth() - $offsetX;
                $y = ceil(($imageBox->getHeight() - $box->getHeight()) / 2);
                break;
            case 'southEast':
                $x = $imageBox->getWidth() - $box->getWidth() - $offsetX;
                $y = $imageBox->getHeight() - $box->getHeight() - $offsetY;
                break;
            case 'center':
            default:
                $x = ceil(($imageBox->getWidth() - $box->getWidth()) / 2);
                $y = ceil(($imageBox->getHeight() - $box->getHeight()) / 2);
        }
        return new Point($x, $y);
    }
}